// Copyright (C) ZHENG Zhong <heavyzheng nospam-at gmail D0T com>
//
// $Id: attribute_map.cpp 317 2009-01-12 11:10:14Z heavyzheng $
//

#ifndef ZZHENG_XML_SOURCE
#define ZZHENG_XML_SOURCE
#endif // ZZHENG_XML_SOURCE

#include <zzheng/xml/dom/libxml2/attribute_map.hpp>
#include <zzheng/xml/dom/libxml2/element.hpp>
#include <zzheng/xml/dom/libxml2/utility.hpp>

#include <libxml/tree.h>
#include <utility>
#include <string>

namespace zzheng {
namespace xml {
namespace dom {
namespace libxml2_ {

    attribute_map::attribute_map(xmlNode* px): raw_(px) {
        assert(raw()->type == XML_ELEMENT_NODE);
    }

    attribute_map::~attribute_map() {
        raw_ = 0;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // capacity
    //

    bool attribute_map::empty() const {
        return (first_() == 0);
    }

    size_type attribute_map::size() const {
        size_type count = 0;
        for (const_iterator i = begin(); i != end(); ++i) {
            ++count;
        }
        return count;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // iterators
    //

    attribute_map::iterator attribute_map::begin() {
        return iterator( const_cast<attribute*>(first_()) );
    }

    attribute_map::iterator attribute_map::end() {
        return iterator();
    }

    attribute_map::const_iterator attribute_map::begin() const {
        return const_iterator( first_() );
    }

    attribute_map::const_iterator attribute_map::end() const {
        return const_iterator();
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // find
    //

    attribute_map::iterator attribute_map::find(const std::string& name) {
        return iterator( const_cast<attribute*>(find_(name, std::string())) );
    }

    attribute_map::const_iterator attribute_map::find(const std::string& name) const {
        return const_iterator( find_(name, std::string()) );
    }

    attribute_map::iterator attribute_map::find(const std::string& name,
                                                const std::string& uri) {
        return iterator( const_cast<attribute*>(find_(name, uri)) );
    }

    attribute_map::const_iterator attribute_map::find(const std::string& name,
                                                      const std::string& uri) const {
        return const_iterator( find_(name, uri) );
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // modifiers
    //

    std::pair<basic_node_ptr<attribute>, bool> attribute_map::insert(const attribute_proxy& attr) {
        iterator existing = find(attr.name(), attr.uri());
        if (existing == end()) {
            attribute* inserted = create_attr_(attr);
            assert(inserted != 0);
            return std::make_pair(basic_node_ptr<attribute>(inserted), true);
        } else {
            return std::make_pair(existing.ptr(), false);
        }
    }

    basic_node_ptr<attribute> attribute_map::insert_update(const attribute_proxy& attr) {
        std::pair<basic_node_ptr<attribute>, bool> inserted = insert(attr);
        if (!inserted.second) {
            inserted.first->set_value(attr.value());
        }
        return inserted.first;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // private
    //

    element& attribute_map::owner() {
        return const_cast<element&>( (static_cast<const attribute_map&>(*this)).owner() );
    }

    const element& attribute_map::owner() const {
        const element* owner = static_cast<const element*>(raw()->_private);
        assert(owner != 0 && "Owner element should not be null.");
        return *owner;
    }

    const attribute* attribute_map::first_() const {
        if (raw()->properties != 0) {
            return static_cast<const attribute*>(raw()->properties->_private);
        } else {
            return 0;
        }
    }

    const attribute* attribute_map::find_(const std::string& name, const std::string& uri) const {
        const attribute* found = 0;
        for (const xmlAttr* i = raw()->properties; found == 0 && i != 0; i = i->next) {
            const attribute* attr = static_cast<const attribute*>(i->_private);
            if (attr->name() == name && attr->uri() == uri) {
                found = attr;
            }
        }
        return found;
    }

    attribute* attribute_map::create_attr_(const attribute_proxy& attr) {
        // Declare the attribute's namespace URI on the owner element.
        xmlNs* ns = 0;
        if (!attr.uri().empty()) {
            ns = owner().declare_namespace_(attr.uri());
            assert(ns != 0);
        }
        // Create the libxml2 attribute for the owner element, with namespace.
        xmlAttr* px = xmlNewNsProp( raw(),
                                    ns,
                                    to_xml_chars(attr.name().c_str()),
                                    to_xml_chars(attr.value().c_str()) );
        // TODO: how if an attribute with similar name and URI exists?
        if (px == 0) {
            return 0;
        } else {
            return static_cast<attribute*>(px->_private);
        }
    }


} // namespace zzheng::xml::dom::libxml2_
} // namespace zzheng::xml::dom
} // namespace zzheng::xml
} // namespace zzheng




